---
title: Health Checks
subtitle: Determining the router's status
description: Learn how to run health checks to determine whether an Apollo GraphOS Router or Apollo Router Core is available and ready to start serving traffic.
---

Health checks are often used by load balancers to determine whether a server is available and ready to start serving traffic.

GraphOS Router and Apollo Router Core support a basic HTTP-level health check. This is enabled by default and is served on port `8088` at the URL path `/health`. This returns a `200` status code if the HTTP server is successfully serving.
You can change this by setting `health_check`:
```yaml title="router.yaml"
health_check:
  listen: 127.0.0.1:8088
  enabled: true
  path: /health # Optional, default: /health
```

Each option is configurable. For example, we can set our health check endpoint to `127.0.0.1:8090/healthz`:

```yaml title="router.yaml"
health_check:
  listen: 127.0.0.1:8090
  enabled: true
  path: /healthz
```

We can also disable the health check endpoint:

```yaml title="router.yaml"
health_check:
  enabled: false
```

## Testing with `curl`

The following example demonstrates using the `curl` command to send a basic health check query to a router instance running at `127.0.0.1:4000`:

```sh
$ curl -v "http://127.0.0.1:8088/health"
*   Trying 127.0.0.1:8088...
* Connected to 127.0.0.1 (127.0.0.1) port 8088 (#0)
> GET /health HTTP/1.1
> Host: 127.0.0.1:8088
> User-Agent: curl/7.79.1
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< vary: origin
< content-type: application/json
< content-length: 15
< date: Wed, 21 Sep 2022 17:10:45 GMT
< 
* Connection #0 to host 127.0.0.1 left intact
{"status":"UP"}
```

## Logging

If you start the router with trace logging enabled, you will see a log from the router for each health check:

```sh
--log apollo_router=trace

2023-01-23T17:42:04.640501Z apollo-router/src/axum_factory/axum_http_server_factory.rs:100 TRACE apollo_router::axum_factory::axum_http_server_factory: health check health=Health { status: Up } request=Request { method: GET, uri: /health, version: HTTP/1.1, headers: {"host": "127.0.0.1:8088", "user-agent": "curl/7.85.0", "accept": "*/*"}, body: Body(Empty) }

```

This may be helpful with confirming that health-checks are working correctly.

## Using in a containers environment

The health check listens to 127.0.0.1 by default, which won't allow connections issued from a network.
While this is a safe default, *other containers won't be able to perform health checks*, which will prevent the router pod from switching to a healthy state.

You can change this by setting `health_check`:
```yaml title="router.yaml"
health_check:
  listen: 0.0.0.0:8088
  enabled: true
```

## Using with Kubernetes
In Kubernetes, you can configure health checks by setting `readinessProbe` and `livenessProbe` on the `containers` object of the resource definition:
```yaml
      # ... snipped for partial example ...
      containers:
        - name: router
          # ... snipped for partial example ...
          livenessProbe:
            httpGet:
              path: "/health?live"
              port: 8088
          readinessProbe:
            httpGet:
              path: "/health?ready"
              port: 8088
          # ... snipped for partial example ...
```

See a more complete example in our [Kubernetes documentation](/router/containerization/kubernetes/).

<Note>
For these checks we take advantage of additional functionality in the router which enables specific "ready" and "live" checks to better support kubernetes deployments. For each check, if the router is live or ready it will return OK (200). If not, it will return Service Unavailable (503).
</Note>

### Liveness

Liveness is clearly defined in Router 2 as the point at which a router configuration has been activated. From this point onwards, the router will remain Live unless the endpoint stops responding.

### Readiness

Readiness is clearly defined in Router 2 as the point at which a router configuration has been activated. From this point onwards, the router will monitor responses and identify over-loading. If over-loading passes beyond a defined tolerance, the router will declare itself unready for a period of time. During this time, it will continue to service requests and when the unready period expires, the router will once more start to monitor for over-loading. This is all controlled by new configuration in the router health check.

```yaml title="router.yaml"
health_check:
  listen: 0.0.0.0:8088
  enabled: true
  readiness:        # optional, with default as detailed below
    allowed: 50     # optional, default 100
    interval:
      sampling: 5s # optional, default 5s
      unready: 10s  # optional, default (2 * sampling)
```

In this snippet, readiness has been configured to allow 50 rejections due to load shedding (GATEWAY_TIMEOUT or SERVICE_UNAVAILABLE) in each sampling interval (10 seconds). If the router determines that it is "unready", i.e.: these limits are exceeded, then it will indicate this status (SERVICE_UNAVAILABLE) via the `readinessProbe` for the unready interval (30 seconds). Once this interval has passed, it will return to "ready" and start sampling responses.

<Note>
The default sampling and unready intervals are chosen to align with the defaults for Kubernetes readinessProbe interval (10s). The idea being that there is sampling within a default interval and that the unready interval matches the probe perdiod.
</Note>

## Using with Docker
Docker has a `HEALTHCHECK` instruction that tells Docker how to test whether a container is still working. These are defined in the `Dockerfile` when building your container:
```
HEALTHCHECK CMD curl --fail \
  "http://127.0.0.1:8088/health" || exit 1
```
We don't define these in our example `Dockerfile`s, because they aren't commonly used. You can add them to your own images as needed.
